1 /*
2  * The MIT License (MIT)
3  *
4  * Copyright (c) 2014 Devisualization (Richard Andrew Cattermole)
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in all
14  * copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 module devisualization.window.interfaces.eventable;
25 import std..string : toUpper;
26 import std.algorithm : filter, moveAll;
27 
28 /**
29  * Provides an interface version of an eventing mechanism.
30  * 
31  * See_Also:
32  *         Eventing
33  */
34 mixin template IEventing(string name, T...) {
35     mixin("void add" ~ toUpper(name[0] ~ "") ~ name[1 ..$] ~ q{(bool delegate(T));});
36     mixin("void add" ~ toUpper(name[0] ~ "") ~ name[1 ..$] ~ q{(void delegate(T));});
37 
38     mixin("void remove" ~ toUpper(name[0] ~ "") ~ name[1 ..$] ~ q{(bool delegate(T));});
39     mixin("void remove" ~ toUpper(name[0] ~ "") ~ name[1 ..$] ~ q{(void delegate(T));});
40     
41     mixin("size_t count" ~ toUpper(name[0] ~ "") ~ name[1 ..$] ~ "();");
42 
43     mixin("void clear" ~ toUpper(name[0] ~ "") ~ name[1 ..$] ~ q{();});
44     
45     static if (__traits(compiles, typeof(this)) && is(typeof(this) : T[0])) {
46         mixin("void " ~ name ~ q{(T[1 .. $] args);});
47 	} else {
48 		mixin("void " ~ name ~ q{(T args);});
49 	}
50 }
51 
52 
53 /**
54  * Implements an eventable interface for something.
55  * Includes support for bool delegate(T) and void delegate(T).
56  * Will consume a call to all delegates if it returns true. Default false.
57  * 
58  * Example usage:
59  *         mixin Eventing!("onNewListing", ListableObject);
60  * 
61  * If is(T[0] == typeof(this)) then it'll use this as being the first argument. 
62  */
63 mixin template Eventing(string name, T...) {
64 	private {
65 		mixin(q{bool delegate(T)[] } ~ name ~ "_;");
66 		mixin(q{bool delegate(T)[size_t] } ~ name ~ "_assoc;");
67 		
68 		union ptrToSizeT {
69 			void delegate(T) from;
70 			size_t value;
71 		}
72 	}
73 	
74 	mixin("void add" ~ toUpper(name[0] ~ "") ~ name[1 ..$] ~ q{(void delegate(T) value) {
75 				mixin(name ~ "_ ~= (T args) => {value(args); return false;}();");
76 				mixin(name ~ "_assoc[ptrToSizeT(value).value] = " ~ name ~ "_[$-1];");
77 			}});
78 	
79 	mixin("void add" ~ toUpper(name[0] ~ "") ~ name[1 ..$] ~ q{(bool delegate(T) value) {
80 				mixin(name ~ "_ ~= value;"); 
81 			}});
82 	
83 	mixin("void remove" ~ toUpper(name[0] ~ "") ~ name[1 ..$] ~ q{(bool delegate(T) value) {
84 				import std.range : walkLength;
85 				
86 				mixin("auto t = filter!(a => a !is value)(" ~ name ~ "_);");
87 				t.moveAll(mixin(name ~ "_"));
88 				mixin(name ~ "_.length = t.walkLength;");
89 			}});
90 	
91 	mixin("void remove" ~ toUpper(name[0] ~ "") ~ name[1 ..$] ~ q{(void delegate(T) value) {
92 				if (ptrToSizeT(value).value in mixin(name ~ "_assoc")) {
93 					mixin("remove" ~ toUpper(name[0] ~ "") ~ name[1 ..$] ~ "(" ~ name ~ "_assoc[ptrToSizeT(value).value]);");
94 					mixin(name ~ "_assoc.remove(ptrToSizeT(value).value);");
95 				}
96 			}});
97 	
98 	mixin("size_t count" ~ toUpper(name[0] ~ "") ~ name[1 ..$] ~ q{(){
99 				return cast(size_t)(mixin(name ~ "_.length") + mixin(name ~ "_assoc.length")); 
100 			}});
101 	
102 	mixin("void clear" ~ toUpper(name[0] ~ "") ~ name[1 ..$] ~ q{() {
103 				mixin(name ~ "_") = [];
104 			}});
105 	
106 	static if (__traits(compiles, typeof(this)) && is(typeof(this) : T[0])) {
107 		mixin("void " ~ name ~ q{(T[1 .. $] args) {
108 					foreach (del; mixin(name ~ "_")) {
109 						if (del(this, args))
110 							return;
111 					}
112 				}});
113 	} else {
114 		mixin("void " ~ name ~ q{(T args) {
115 					foreach (del; mixin(name ~ "_")) {
116 						if (del(args))
117 							return;
118 					}
119 				}});
120 	}
121 }